﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TimbreRecognition.Recognition.Helper;
using TimbreRecognition.Recognition.Model.Kohenen;

namespace TimbreRecognition.Recognition.Teacher.Kohenen
{
   public class IterationKohenenTeacher : AbstractKohenenTeacher {

        private readonly static int T = 50;

        //rapidity
        private readonly static double N = 0.1;

        private readonly static double E = 0.05;

        private ILogger logger;

        private int iterationNumber;

        private int s0;

        public override void teach(KohenenNetwork network, List<double[]> data)
        {

            s0 = (int) Math.Floor((double)Math.Min(network.getXDimension(), network.getYDimension()) / 2 );

            iterationNumber = 0;

            int inputCount = data[0].Length;
            double averageWeighError;

            do {

                network.resetWinCount();

                double totalError = 0;

                foreach (double[] input in data) {

                    network.recalculateOutput(input);
                    totalError += getError(network, input);
                    changeWeight(network, input);
                }

                iterationNumber++;

                averageWeighError = totalError / data.Count / inputCount;

                Log("Iteration = " + iterationNumber + ", average weight error = " + averageWeighError);
            } while (iterationNumber < T );

            NeuronNetworkHelper.CalculeteWinCount(network, data);
        }

        private void changeWeight(KohenenNetwork network, double[] input) {

            KohenenNeuron winner = network.getWinner();
            winner.incWinCount();

            double s = s0 * Math.Exp(-s0 * (double)iterationNumber / T);

            double n = N * Math.Exp(-(double) iterationNumber / T);

            foreach (KohenenNeuron[] neurons in network.getNeurons()) {
                foreach (KohenenNeuron neuron in neurons) {

                    double distance = getDistance(winner, neuron);
                    double h = Math.Exp(-distance*distance / (2 * Math.Pow(s, 2)));

                    double[] weights = neuron.getWeights();
                    for (int i = 0; i < weights.Length; i++){

                        double weight = weights[i];
                        double inputValue = input[i];

                        double delta = n * h * (inputValue - weight);
                        weights[i] += delta;
                    }
                }
            }
        }

        public override void SetLogger(ILogger logger)
        {
            this.logger = logger;
        }

        private void Log(string message)
        {
            if (logger != null)
            {
                logger.Log(message);
            }
        }
    }
}
